home *** CD-ROM | disk | FTP | other *** search
/ Maximum CD 2000 March / maximum-cd-2000-03.iso / Quake3 Game Source / Q3AGameSource.exe / Main / g_active.c < prev    next >
Encoding:
C/C++ Source or Header  |  2000-01-18  |  20.9 KB  |  851 lines

  1. // Copyright (C) 1999-2000 Id Software, Inc.
  2. //
  3.  
  4. #include "g_local.h"
  5.  
  6.  
  7. /*
  8. ===============
  9. G_DamageFeedback
  10.  
  11. Called just before a snapshot is sent to the given player.
  12. Totals up all damage and generates both the player_state_t
  13. damage values to that client for pain blends and kicks, and
  14. global pain sound events for all clients.
  15. ===============
  16. */
  17. void P_DamageFeedback( gentity_t *player ) {
  18.     gclient_t    *client;
  19.     float    count;
  20.     vec3_t    angles;
  21.  
  22.     client = player->client;
  23.     if ( client->ps.pm_type == PM_DEAD ) {
  24.         return;
  25.     }
  26.  
  27.     // total points of damage shot at the player this frame
  28.     count = client->damage_blood + client->damage_armor;
  29.     if ( count == 0 ) {
  30.         return;        // didn't take any damage
  31.     }
  32.  
  33.     if ( count > 255 ) {
  34.         count = 255;
  35.     }
  36.  
  37.     // send the information to the client
  38.  
  39.     // world damage (falling, slime, etc) uses a special code
  40.     // to make the blend blob centered instead of positional
  41.     if ( client->damage_fromWorld ) {
  42.         client->ps.damagePitch = 255;
  43.         client->ps.damageYaw = 255;
  44.  
  45.         client->damage_fromWorld = qfalse;
  46.     } else {
  47.         vectoangles( client->damage_from, angles );
  48.         client->ps.damagePitch = angles[PITCH]/360.0 * 256;
  49.         client->ps.damageYaw = angles[YAW]/360.0 * 256;
  50.     }
  51.  
  52.     // play an apropriate pain sound
  53.     if ( (level.time > player->pain_debounce_time) && !(player->flags & FL_GODMODE) ) {
  54.         player->pain_debounce_time = level.time + 700;
  55.         G_AddEvent( player, EV_PAIN, player->health );
  56.         client->ps.damageEvent++;
  57.     }
  58.  
  59.  
  60.     client->ps.damageCount = count;
  61.  
  62.     //
  63.     // clear totals
  64.     //
  65.     client->damage_blood = 0;
  66.     client->damage_armor = 0;
  67.     client->damage_knockback = 0;
  68. }
  69.  
  70.  
  71.  
  72. /*
  73. =============
  74. P_WorldEffects
  75.  
  76. Check for lava / slime contents and drowning
  77. =============
  78. */
  79. void P_WorldEffects( gentity_t *ent ) {
  80.     qboolean    envirosuit;
  81.     int            waterlevel;
  82.  
  83.     if ( ent->client->noclip ) {
  84.         ent->client->airOutTime = level.time + 12000;    // don't need air
  85.         return;
  86.     }
  87.  
  88.     waterlevel = ent->waterlevel;
  89.  
  90.     envirosuit = ent->client->ps.powerups[PW_BATTLESUIT] > level.time;
  91.  
  92.     //
  93.     // check for drowning
  94.     //
  95.     if ( waterlevel == 3 ) {
  96.         // envirosuit give air
  97.         if ( envirosuit ) {
  98.             ent->client->airOutTime = level.time + 10000;
  99.         }
  100.  
  101.         // if out of air, start drowning
  102.         if ( ent->client->airOutTime < level.time) {
  103.             // drown!
  104.             ent->client->airOutTime += 1000;
  105.             if ( ent->health > 0 ) {
  106.                 // take more damage the longer underwater
  107.                 ent->damage += 2;
  108.                 if (ent->damage > 15)
  109.                     ent->damage = 15;
  110.  
  111.                 // play a gurp sound instead of a normal pain sound
  112.                 if (ent->health <= ent->damage) {
  113.                     G_Sound(ent, CHAN_VOICE, G_SoundIndex("*drown.wav"));
  114.                 } else if (rand()&1) {
  115.                     G_Sound(ent, CHAN_VOICE, G_SoundIndex("sound/player/gurp1.wav"));
  116.                 } else {
  117.                     G_Sound(ent, CHAN_VOICE, G_SoundIndex("sound/player/gurp2.wav"));
  118.                 }
  119.  
  120.                 // don't play a normal pain sound
  121.                 ent->pain_debounce_time = level.time + 200;
  122.  
  123.                 G_Damage (ent, NULL, NULL, NULL, NULL, 
  124.                     ent->damage, DAMAGE_NO_ARMOR, MOD_WATER);
  125.             }
  126.         }
  127.     } else {
  128.         ent->client->airOutTime = level.time + 12000;
  129.         ent->damage = 2;
  130.     }
  131.  
  132.     //
  133.     // check for sizzle damage (move to pmove?)
  134.     //
  135.     if (waterlevel && 
  136.         (ent->watertype&(CONTENTS_LAVA|CONTENTS_SLIME)) ) {
  137.         if (ent->health > 0
  138.             && ent->pain_debounce_time <= level.time    ) {
  139.  
  140.             if ( envirosuit ) {
  141.                 G_AddEvent( ent, EV_POWERUP_BATTLESUIT, 0 );
  142.             } else {
  143.                 if (ent->watertype & CONTENTS_LAVA) {
  144.                     G_Damage (ent, NULL, NULL, NULL, NULL, 
  145.                         30*waterlevel, 0, MOD_LAVA);
  146.                 }
  147.  
  148.                 if (ent->watertype & CONTENTS_SLIME) {
  149.                     G_Damage (ent, NULL, NULL, NULL, NULL, 
  150.                         10*waterlevel, 0, MOD_SLIME);
  151.                 }
  152.             }
  153.         }
  154.     }
  155. }
  156.  
  157.  
  158.  
  159. /*
  160. ===============
  161. G_SetClientSound
  162. ===============
  163. */
  164. void G_SetClientSound( gentity_t *ent ) {
  165.     if (ent->waterlevel && (ent->watertype&(CONTENTS_LAVA|CONTENTS_SLIME)) )
  166.         ent->s.loopSound = level.snd_fry;
  167.     else
  168.         ent->s.loopSound = 0;
  169. }
  170.  
  171.  
  172.  
  173. //==============================================================
  174.  
  175. /*
  176. ==============
  177. ClientImpacts
  178. ==============
  179. */
  180. void ClientImpacts( gentity_t *ent, pmove_t *pm ) {
  181.     int        i, j;
  182.     trace_t    trace;
  183.     gentity_t    *other;
  184.  
  185.     memset( &trace, 0, sizeof( trace ) );
  186.     for (i=0 ; i<pm->numtouch ; i++) {
  187.         for (j=0 ; j<i ; j++) {
  188.             if (pm->touchents[j] == pm->touchents[i] ) {
  189.                 break;
  190.             }
  191.         }
  192.         if (j != i) {
  193.             continue;    // duplicated
  194.         }
  195.         other = &g_entities[ pm->touchents[i] ];
  196.  
  197.         if ( ( ent->r.svFlags & SVF_BOT ) && ( ent->touch ) ) {
  198.             ent->touch( ent, other, &trace );
  199.         }
  200.  
  201.         if ( !other->touch ) {
  202.             continue;
  203.         }
  204.  
  205.         other->touch( other, ent, &trace );
  206.     }
  207.  
  208. }
  209.  
  210. /*
  211. ============
  212. G_TouchTriggers
  213.  
  214. Find all trigger entities that ent's current position touches.
  215. Spectators will only interact with teleporters.
  216. ============
  217. */
  218. void    G_TouchTriggers( gentity_t *ent ) {
  219.     int            i, num;
  220.     int            touch[MAX_GENTITIES];
  221.     gentity_t    *hit;
  222.     trace_t        trace;
  223.     vec3_t        mins, maxs;
  224.     static vec3_t    range = { 40, 40, 52 };
  225.  
  226.     if ( !ent->client ) {
  227.         return;
  228.     }
  229.  
  230.     // dead clients don't activate triggers!
  231.     if ( ent->client->ps.stats[STAT_HEALTH] <= 0 ) {
  232.         return;
  233.     }
  234.  
  235.     VectorSubtract( ent->client->ps.origin, range, mins );
  236.     VectorAdd( ent->client->ps.origin, range, maxs );
  237.  
  238.     num = trap_EntitiesInBox( mins, maxs, touch, MAX_GENTITIES );
  239.  
  240.     // can't use ent->absmin, because that has a one unit pad
  241.     VectorAdd( ent->client->ps.origin, ent->r.mins, mins );
  242.     VectorAdd( ent->client->ps.origin, ent->r.maxs, maxs );
  243.  
  244.     for ( i=0 ; i<num ; i++ ) {
  245.         hit = &g_entities[touch[i]];
  246.  
  247.         if ( !hit->touch && !ent->touch ) {
  248.             continue;
  249.         }
  250.         if ( !( hit->r.contents & CONTENTS_TRIGGER ) ) {
  251.             continue;
  252.         }
  253.  
  254.         // ignore most entities if a spectator
  255.         if ( ent->client->sess.sessionTeam == TEAM_SPECTATOR ) {
  256.             if ( hit->s.eType != ET_TELEPORT_TRIGGER &&
  257.                 // this is ugly but adding a new ET_? type will
  258.                 // most likely cause network incompatibilities
  259.                 hit->touch != Touch_DoorTrigger) {
  260.                 continue;
  261.             }
  262.         }
  263.  
  264.         // use seperate code for determining if an item is picked up
  265.         // so you don't have to actually contact its bounding box
  266.         if ( hit->s.eType == ET_ITEM ) {
  267.             if ( !BG_PlayerTouchesItem( &ent->client->ps, &hit->s, level.time ) ) {
  268.                 continue;
  269.             }
  270.         } else {
  271.             if ( !trap_EntityContact( mins, maxs, hit ) ) {
  272.                 continue;
  273.             }
  274.         }
  275.  
  276.         memset( &trace, 0, sizeof(trace) );
  277.  
  278.         if ( hit->touch ) {
  279.             hit->touch (hit, ent, &trace);
  280.         }
  281.  
  282.         if ( ( ent->r.svFlags & SVF_BOT ) && ( ent->touch ) ) {
  283.             ent->touch( ent, hit, &trace );
  284.         }
  285.     }
  286. }
  287.  
  288. /*
  289. =================
  290. SpectatorThink
  291. =================
  292. */
  293. void SpectatorThink( gentity_t *ent, usercmd_t *ucmd ) {
  294.     pmove_t    pm;
  295.     gclient_t    *client;
  296.  
  297.     client = ent->client;
  298.  
  299.     if ( client->sess.spectatorState != SPECTATOR_FOLLOW ) {
  300.         client->ps.pm_type = PM_SPECTATOR;
  301.         client->ps.speed = 400;    // faster than normal
  302.  
  303.         // set up for pmove
  304.         memset (&pm, 0, sizeof(pm));
  305.         pm.ps = &client->ps;
  306.         pm.cmd = *ucmd;
  307.         pm.tracemask = MASK_PLAYERSOLID & ~CONTENTS_BODY;    // spectators can fly through bodies
  308.         pm.trace = trap_Trace;
  309.         pm.pointcontents = trap_PointContents;
  310.  
  311.         // perform a pmove
  312.         Pmove (&pm);
  313.  
  314.         // save results of pmove
  315.         VectorCopy( client->ps.origin, ent->s.origin );
  316.  
  317.         G_TouchTriggers( ent );
  318.         trap_UnlinkEntity( ent );
  319.     }
  320.  
  321.     client->oldbuttons = client->buttons;
  322.     client->buttons = ucmd->buttons;
  323.  
  324.     // attack button cycles through spectators
  325.     if ( ( client->buttons & BUTTON_ATTACK ) && ! ( client->oldbuttons & BUTTON_ATTACK ) ) {
  326.         Cmd_FollowCycle_f( ent, 1 );
  327.     }
  328. }
  329.  
  330.  
  331.  
  332. /*
  333. =================
  334. ClientInactivityTimer
  335.  
  336. Returns qfalse if the client is dropped
  337. =================
  338. */
  339. qboolean ClientInactivityTimer( gclient_t *client ) {
  340.     if ( ! g_inactivity.integer ) {
  341.         // give everyone some time, so if the operator sets g_inactivity during
  342.         // gameplay, everyone isn't kicked
  343.         client->inactivityTime = level.time + 60 * 1000;
  344.         client->inactivityWarning = qfalse;
  345.     } else if ( client->pers.cmd.forwardmove || 
  346.         client->pers.cmd.rightmove || 
  347.         client->pers.cmd.upmove ||
  348.         (client->pers.cmd.buttons & BUTTON_ATTACK) ) {
  349.         client->inactivityTime = level.time + g_inactivity.integer * 1000;
  350.         client->inactivityWarning = qfalse;
  351.     } else if ( !client->pers.localClient ) {
  352.         if ( level.time > client->inactivityTime ) {
  353.             trap_DropClient( client - level.clients, "Dropped due to inactivity" );
  354.             return qfalse;
  355.         }
  356.         if ( level.time > client->inactivityTime - 10000 && !client->inactivityWarning ) {
  357.             client->inactivityWarning = qtrue;
  358.             trap_SendServerCommand( client - level.clients, "cp \"Ten seconds until inactivity drop!\n\"" );
  359.         }
  360.     }
  361.     return qtrue;
  362. }
  363.  
  364. /*
  365. ==================
  366. ClientTimerActions
  367.  
  368. Actions that happen once a second
  369. ==================
  370. */
  371. void ClientTimerActions( gentity_t *ent, int msec ) {
  372.     gclient_t *client;
  373.  
  374.     client = ent->client;
  375.     client->timeResidual += msec;
  376.  
  377.     while ( client->timeResidual >= 1000 ) {
  378.         client->timeResidual -= 1000;
  379.  
  380.         // regenerate
  381.         if ( client->ps.powerups[PW_REGEN] ) {
  382.             if ( ent->health < client->ps.stats[STAT_MAX_HEALTH]) {
  383.                 ent->health += 15;
  384.                 if ( ent->health > client->ps.stats[STAT_MAX_HEALTH] * 1.1 ) {
  385.                     ent->health = client->ps.stats[STAT_MAX_HEALTH] * 1.1;
  386.                 }
  387.                 G_AddEvent( ent, EV_POWERUP_REGEN, 0 );
  388.             } else if ( ent->health < client->ps.stats[STAT_MAX_HEALTH] * 2) {
  389.                 ent->health += 5;
  390.                 if ( ent->health > client->ps.stats[STAT_MAX_HEALTH] * 2 ) {
  391.                     ent->health = client->ps.stats[STAT_MAX_HEALTH] * 2;
  392.                 }
  393.                 G_AddEvent( ent, EV_POWERUP_REGEN, 0 );
  394.             }
  395.         } else {
  396.             // count down health when over max
  397.             if ( ent->health > client->ps.stats[STAT_MAX_HEALTH] ) {
  398.                 ent->health--;
  399.             }
  400.         }
  401.  
  402.         // count down armor when over max
  403.         if ( client->ps.stats[STAT_ARMOR] > client->ps.stats[STAT_MAX_HEALTH] ) {
  404.             client->ps.stats[STAT_ARMOR]--;
  405.         }
  406.  
  407.     }
  408. }
  409.  
  410. /*
  411. ====================
  412. ClientIntermissionThink
  413. ====================
  414. */
  415. void ClientIntermissionThink( gclient_t *client ) {
  416.     client->ps.eFlags &= ~EF_TALK;
  417.     client->ps.eFlags &= ~EF_FIRING;
  418.  
  419.     // the level will exit when everyone wants to or after timeouts
  420.  
  421.     // swap and latch button actions
  422.     client->oldbuttons = client->buttons;
  423.     client->buttons = client->pers.cmd.buttons;
  424.     if ( client->buttons & ( BUTTON_ATTACK | BUTTON_USE_HOLDABLE ) & ( client->oldbuttons ^ client->buttons ) ) {
  425.         client->readyToExit ^= 1;
  426.     }
  427. }
  428.  
  429.  
  430. /*
  431. ================
  432. ClientEvents
  433.  
  434. Events will be passed on to the clients for presentation,
  435. but any server game effects are handled here
  436. ================
  437. */
  438. void ClientEvents( gentity_t *ent, int oldEventSequence ) {
  439.     int        i;
  440.     int        event;
  441.     gclient_t *client;
  442.     int        damage;
  443.     vec3_t    dir;
  444.     vec3_t    origin, angles;
  445. //    qboolean    fired;
  446.     gitem_t *item;
  447.     gentity_t *drop;
  448.  
  449.     client = ent->client;
  450.  
  451.     if ( oldEventSequence < client->ps.eventSequence - MAX_PS_EVENTS ) {
  452.         oldEventSequence = client->ps.eventSequence - MAX_PS_EVENTS;
  453.     }
  454.     for ( i = oldEventSequence ; i < client->ps.eventSequence ; i++ ) {
  455.         event = client->ps.events[ i & (MAX_PS_EVENTS-1) ];
  456.  
  457.         switch ( event ) {
  458.         case EV_FALL_MEDIUM:
  459.         case EV_FALL_FAR:
  460.             if ( ent->s.eType != ET_PLAYER ) {
  461.                 break;        // not in the player model
  462.             }
  463.             if ( g_dmflags.integer & DF_NO_FALLING ) {
  464.                 break;
  465.             }
  466.             if ( event == EV_FALL_FAR ) {
  467.                 damage = 10;
  468.             } else {
  469.                 damage = 5;
  470.             }
  471.             VectorSet (dir, 0, 0, 1);
  472.             ent->pain_debounce_time = level.time + 200;    // no normal pain sound
  473.             G_Damage (ent, NULL, NULL, NULL, NULL, damage, 0, MOD_FALLING);
  474.             break;
  475.  
  476.         case EV_FIRE_WEAPON:
  477.             FireWeapon( ent );
  478.             break;
  479.  
  480.         case EV_USE_ITEM1:        // teleporter
  481.             // drop flags in CTF
  482.             item = NULL;
  483.  
  484.             if ( ent->client->ps.powerups[ PW_REDFLAG ] ) {
  485.                 item = BG_FindItemForPowerup( PW_REDFLAG );
  486.                 i = PW_REDFLAG;
  487.             } else if ( ent->client->ps.powerups[ PW_BLUEFLAG ] ) {
  488.                 item = BG_FindItemForPowerup( PW_BLUEFLAG );
  489.                 i = PW_BLUEFLAG;
  490.             }
  491.  
  492.             if ( item ) {
  493.                 drop = Drop_Item( ent, item, 0 );
  494.                 // decide how many seconds it has left
  495.                 drop->count = ( ent->client->ps.powerups[ i ] - level.time ) / 1000;
  496.                 if ( drop->count < 1 ) {
  497.                     drop->count = 1;
  498.                 }
  499.  
  500.                 ent->client->ps.powerups[ i ] = 0;
  501.             }
  502.  
  503.             SelectSpawnPoint( ent->client->ps.origin, origin, angles );
  504.             TeleportPlayer( ent, origin, angles );
  505.             break;
  506.  
  507.         case EV_USE_ITEM2:        // medkit
  508.             ent->health = ent->client->ps.stats[STAT_MAX_HEALTH];
  509.             break;
  510.  
  511.         default:
  512.             break;
  513.         }
  514.     }
  515.  
  516. }
  517.  
  518. void BotTestSolid(vec3_t origin);
  519.  
  520. /*
  521. ==============
  522. ClientThink
  523.  
  524. This will be called once for each client frame, which will
  525. usually be a couple times for each server frame on fast clients.
  526.  
  527. If "g_syncronousClients 1" is set, this will be called exactly
  528. once for each server frame, which makes for smooth demo recording.
  529. ==============
  530. */
  531. void ClientThink_real( gentity_t *ent ) {
  532.     gclient_t    *client;
  533.     pmove_t        pm;
  534.     vec3_t        oldOrigin;
  535.     int            oldEventSequence;
  536.     int            msec;
  537.     usercmd_t    *ucmd;
  538.  
  539.     client = ent->client;
  540.  
  541.     // mark the time, so the connection sprite can be removed
  542.     ucmd = &ent->client->pers.cmd;
  543.  
  544.     // sanity check the command time to prevent speedup cheating
  545.     if ( ucmd->serverTime > level.time + 200 ) {
  546.         ucmd->serverTime = level.time + 200;
  547. //        G_Printf("serverTime <<<<<\n" );
  548.     }
  549.     if ( ucmd->serverTime < level.time - 1000 ) {
  550.         ucmd->serverTime = level.time - 1000;
  551. //        G_Printf("serverTime >>>>>\n" );
  552.     } 
  553.  
  554.     msec = ucmd->serverTime - client->ps.commandTime;
  555.     // following others may result in bad times, but we still want
  556.     // to check for follow toggles
  557.     if ( msec < 1 && client->sess.spectatorState != SPECTATOR_FOLLOW ) {
  558.         return;
  559.     }
  560.     if ( msec > 200 ) {
  561.         msec = 200;
  562.     }
  563.  
  564.     //
  565.     // check for exiting intermission
  566.     //
  567.     if ( level.intermissiontime ) {
  568.         ClientIntermissionThink( client );
  569.         return;
  570.     }
  571.  
  572.     // spectators don't do much
  573.     if ( client->sess.sessionTeam == TEAM_SPECTATOR ) {
  574.         if ( client->sess.spectatorState == SPECTATOR_SCOREBOARD ) {
  575.             return;
  576.         }
  577.         SpectatorThink( ent, ucmd );
  578.         return;
  579.     }
  580.  
  581.     // check for inactivity timer, but never drop the local client of a non-dedicated server
  582.     if ( !ClientInactivityTimer( client ) ) {
  583.         return;
  584.     }
  585.  
  586.     // clear the rewards if time
  587.     if ( level.time > client->rewardTime ) {
  588.         client->ps.eFlags &= ~(EF_AWARD_IMPRESSIVE | EF_AWARD_EXCELLENT | EF_AWARD_GAUNTLET );
  589.     }
  590.  
  591.     if ( client->noclip ) {
  592.         client->ps.pm_type = PM_NOCLIP;
  593.     } else if ( client->ps.stats[STAT_HEALTH] <= 0 ) {
  594.         client->ps.pm_type = PM_DEAD;
  595.     } else {
  596.         client->ps.pm_type = PM_NORMAL;
  597.     }
  598.  
  599.     client->ps.gravity = g_gravity.value;
  600.  
  601.     // set speed
  602.     client->ps.speed = g_speed.value;
  603.  
  604.     if ( client->ps.powerups[PW_HASTE] ) {
  605.         client->ps.speed *= 1.3;
  606.     }
  607.  
  608.     // Let go of the hook if we aren't firing
  609.     if ( client->ps.weapon == WP_GRAPPLING_HOOK &&
  610.         client->hook && !( ucmd->buttons & BUTTON_ATTACK ) ) {
  611.         Weapon_HookFree(client->hook);
  612.     }
  613.  
  614.     // set up for pmove
  615.     oldEventSequence = client->ps.eventSequence;
  616.  
  617.     memset (&pm, 0, sizeof(pm));
  618.  
  619.     // check for the hit-scan gauntlet, don't let the action
  620.     // go through as an attack unless it actually hits something
  621.     if ( client->ps.weapon == WP_GAUNTLET &&
  622.         ( ucmd->buttons & BUTTON_ATTACK ) && client->ps.weaponTime <= 0 ) {
  623.         pm.gauntletHit = CheckGauntletAttack( ent );
  624.     }
  625.  
  626.     pm.ps = &client->ps;
  627.     pm.cmd = *ucmd;
  628.     if ( pm.ps->pm_type == PM_DEAD ) {
  629.         pm.tracemask = MASK_PLAYERSOLID & ~CONTENTS_BODY;
  630.     }
  631.     else {
  632.         pm.tracemask = MASK_PLAYERSOLID;
  633.     }
  634.     pm.trace = trap_Trace;
  635.     pm.pointcontents = trap_PointContents;
  636.     pm.debugLevel = g_debugMove.integer;
  637.     pm.noFootsteps = ( g_dmflags.integer & DF_NO_FOOTSTEPS ) > 0;
  638.  
  639.     VectorCopy( client->ps.origin, oldOrigin );
  640.  
  641.     // perform a pmove
  642.     Pmove (&pm);
  643.  
  644.     // save results of pmove
  645.     if ( ent->client->ps.eventSequence != oldEventSequence ) {
  646.         ent->eventTime = level.time;
  647.     }
  648.     BG_PlayerStateToEntityState( &ent->client->ps, &ent->s, qtrue );
  649.     if ( !( ent->client->ps.eFlags & EF_FIRING ) ) {
  650.         client->fireHeld = qfalse;        // for grapple
  651.     }
  652.  
  653. #if 1
  654.     // use the precise origin for linking
  655.     VectorCopy( ent->client->ps.origin, ent->r.currentOrigin );
  656. #else
  657.     // use the snapped origin for linking so it matches client predicted versions
  658.     VectorCopy( ent->s.pos.trBase, ent->r.currentOrigin );
  659. #endif
  660.  
  661.     VectorCopy (pm.mins, ent->r.mins);
  662.     VectorCopy (pm.maxs, ent->r.maxs);
  663.  
  664.     ent->waterlevel = pm.waterlevel;
  665.     ent->watertype = pm.watertype;
  666.  
  667.     // execute client events
  668.     ClientEvents( ent, oldEventSequence );
  669.  
  670.     // link entity now, after any personal teleporters have been used
  671.     trap_LinkEntity (ent);
  672.     if ( !ent->client->noclip ) {
  673.         G_TouchTriggers( ent );
  674.     }
  675.  
  676.     //test for solid areas in the AAS file
  677.     BotTestSolid(ent->r.currentOrigin);
  678.  
  679.     // touch other objects
  680.     ClientImpacts( ent, &pm );
  681.  
  682.     // swap and latch button actions
  683.     client->oldbuttons = client->buttons;
  684.     client->buttons = ucmd->buttons;
  685.     client->latched_buttons |= client->buttons & ~client->oldbuttons;
  686.  
  687.     // check for respawning
  688.     if ( client->ps.stats[STAT_HEALTH] <= 0 ) {
  689.         // wait for the attack button to be pressed
  690.         if ( level.time > client->respawnTime ) {
  691.             // forcerespawn is to prevent users from waiting out powerups
  692.             if ( g_forcerespawn.integer > 0 && 
  693.                 ( level.time - client->respawnTime ) > g_forcerespawn.integer * 1000 ) {
  694.                 respawn( ent );
  695.                 return;
  696.             }
  697.         
  698.             // pressing attack or use is the normal respawn method
  699.             if ( ucmd->buttons & ( BUTTON_ATTACK | BUTTON_USE_HOLDABLE ) ) {
  700.                 respawn( ent );
  701.             }
  702.         }
  703.         return;
  704.     }
  705.  
  706.     // perform once-a-second actions
  707.     ClientTimerActions( ent, msec );
  708. }
  709.  
  710. /*
  711. ==================
  712. ClientThink
  713.  
  714. A new command has arrived from the client
  715. ==================
  716. */
  717. void ClientThink( int clientNum ) {
  718.     gentity_t *ent;
  719.  
  720.     ent = g_entities + clientNum;
  721.     trap_GetUsercmd( clientNum, &ent->client->pers.cmd );
  722.  
  723.     // mark the time we got info, so we can display the
  724.     // phone jack if they don't get any for a while
  725.     ent->client->lastCmdTime = level.time;
  726.  
  727.     if ( !g_syncronousClients.integer ) {
  728.         ClientThink_real( ent );
  729.     }
  730. }
  731.  
  732.  
  733. void G_RunClient( gentity_t *ent ) {
  734.     if ( !g_syncronousClients.integer ) {
  735.         return;
  736.     }
  737.     ent->client->pers.cmd.serverTime = level.time;
  738.     ClientThink_real( ent );
  739. }
  740.  
  741.  
  742. /*
  743. ==================
  744. SpectatorClientEndFrame
  745.  
  746. ==================
  747. */
  748. void SpectatorClientEndFrame( gentity_t *ent ) {
  749.     gclient_t    *cl;
  750.  
  751.     // if we are doing a chase cam or a remote view, grab the latest info
  752.     if ( ent->client->sess.spectatorState == SPECTATOR_FOLLOW ) {
  753.         int        clientNum;
  754.  
  755.         clientNum = ent->client->sess.spectatorClient;
  756.  
  757.         // team follow1 and team follow2 go to whatever clients are playing
  758.         if ( clientNum == -1 ) {
  759.             clientNum = level.follow1;
  760.         } else if ( clientNum == -2 ) {
  761.             clientNum = level.follow2;
  762.         }
  763.         if ( clientNum >= 0 ) {
  764.             cl = &level.clients[ clientNum ];
  765.             if ( cl->pers.connected == CON_CONNECTED && cl->sess.sessionTeam != TEAM_SPECTATOR ) {
  766.                 ent->client->ps = cl->ps;
  767.                 ent->client->ps.pm_flags |= PMF_FOLLOW;
  768.                 return;
  769.             } else {
  770.                 // drop them to free spectators unless they are dedicated camera followers
  771.                 if ( ent->client->sess.spectatorClient >= 0 ) {
  772.                     ent->client->sess.spectatorState = SPECTATOR_FREE;
  773.                     ClientBegin( ent->client - level.clients );
  774.                 }
  775.             }
  776.         }
  777.     }
  778.  
  779.     if ( ent->client->sess.spectatorState == SPECTATOR_SCOREBOARD ) {
  780.         ent->client->ps.pm_flags |= PMF_SCOREBOARD;
  781.     } else {
  782.         ent->client->ps.pm_flags &= ~PMF_SCOREBOARD;
  783.     }
  784. }
  785.  
  786. /*
  787. ==============
  788. ClientEndFrame
  789.  
  790. Called at the end of each server frame for each connected client
  791. A fast client will have multiple ClientThink for each ClientEdFrame,
  792. while a slow client may have multiple ClientEndFrame between ClientThink.
  793. ==============
  794. */
  795. void ClientEndFrame( gentity_t *ent ) {
  796.     int            i;
  797.     clientPersistant_t    *pers;
  798.  
  799.     if ( ent->client->sess.sessionTeam == TEAM_SPECTATOR ) {
  800.         SpectatorClientEndFrame( ent );
  801.         return;
  802.     }
  803.  
  804.     pers = &ent->client->pers;
  805.  
  806.     // turn off any expired powerups
  807.     for ( i = 0 ; i < MAX_POWERUPS ; i++ ) {
  808.         if ( ent->client->ps.powerups[ i ] < level.time ) {
  809.             ent->client->ps.powerups[ i ] = 0;
  810.         }
  811.     }
  812.  
  813.     // save network bandwidth
  814. #if 0
  815.     if ( !g_syncronousClients->integer && ent->client->ps.pm_type == PM_NORMAL ) {
  816.         // FIXME: this must change eventually for non-sync demo recording
  817.         VectorClear( ent->client->ps.viewangles );
  818.     }
  819. #endif
  820.  
  821.     //
  822.     // If the end of unit layout is displayed, don't give
  823.     // the player any normal movement attributes
  824.     //
  825.     if ( level.intermissiontime ) {
  826.         return;
  827.     }
  828.  
  829.     // burn from lava, etc
  830.     P_WorldEffects (ent);
  831.  
  832.     // apply all the damage taken this frame
  833.     P_DamageFeedback (ent);
  834.  
  835.     // add the EF_CONNECTION flag if we haven't gotten commands recently
  836.     if ( level.time - ent->client->lastCmdTime > 1000 ) {
  837.         ent->s.eFlags |= EF_CONNECTION;
  838.     } else {
  839.         ent->s.eFlags &= ~EF_CONNECTION;
  840.     }
  841.  
  842.     ent->client->ps.stats[STAT_HEALTH] = ent->health;    // FIXME: get rid of ent->health...
  843.  
  844.     G_SetClientSound (ent);
  845.  
  846.     // set the latest infor
  847.     BG_PlayerStateToEntityState( &ent->client->ps, &ent->s, qtrue );
  848. }
  849.  
  850.  
  851.